#include "../../include/models/canvas_renderer.h"
#include "../../include/helpers/canvas_render_objects.h"
#include "utils/factory_utils.hpp"

namespace ohos_rive {
void CanvasRenderer::save()
{
    // bind m_ohCanvas before calling these methods.
    assert(m_ohCanvas != nullptr);
    OH_Drawing_CanvasSave(m_ohCanvas);
}
void CanvasRenderer::restore()
{
    // bind m_ohCanvas before calling these methods.
    assert(m_ohCanvas != nullptr);
    OH_Drawing_CanvasRestore(m_ohCanvas);
}
void CanvasRenderer::transform(const rive::Mat2D &transform)
{
    // bind m_ohCanvas before calling these methods.
    assert(m_ohCanvas != nullptr);

    OH_Drawing_Matrix *matrix = OH_Drawing_MatrixCreate();
    OH_Drawing_MatrixSetMatrix(matrix, transform.xx(), transform.yx(), transform.tx(), transform.xy(), transform.yy(),
                               transform.ty(), 0, 0, 1);
    OH_Drawing_CanvasConcatMatrix(m_ohCanvas, matrix);
}
void CanvasRenderer::clipPath(rive::RenderPath *path)
{
    // bind m_ohCanvas before calling these methods.
    assert(m_ohCanvas != nullptr);
    auto *canvasPath = static_cast<CanvasRenderPath *>(path);
    OH_Drawing_CanvasClipPath(m_ohCanvas, canvasPath->ohosPath(), DIFFERENCE, true);
}
void CanvasRenderer::drawPath(rive::RenderPath *path, rive::RenderPaint *paint)
{
    // bind m_ohCanvas before calling these methods.
    assert(m_ohCanvas != nullptr);
    auto *canvasPath = static_cast<CanvasRenderPath *>(path);
    OH_Drawing_CanvasDrawPath(m_ohCanvas, canvasPath->ohosPath());
}
void CanvasRenderer::drawImage(const rive::RenderImage *image,
                               const rive::ImageSampler options,
                               rive::BlendMode blendMode,
                               float opacity)
{
    // bind m_ohCanvas before calling these methods.
    assert(m_ohCanvas != nullptr);

    const auto *canvasImage = static_cast<const CanvasRenderImage *>(image);
    OhosPaint *ohosPaint = canvasImage->ohosPaint();
    // Opacity is [0.0f..1.0f] while setAlpha() needs [0..255]
    CanvasRenderPaint::SetPaintAlpha(ohosPaint, static_cast<int>(opacity * 255));
    CanvasRenderPaint::SetBlendMode(ohosPaint, blendMode);

    OH_Drawing_CanvasDrawBitmap(m_ohCanvas, canvasImage->ohosBitmap(), 0.0f, 0.0f);
}

void CanvasRenderer::drawImageMesh(const rive::RenderImage *image,
                                   const rive::ImageSampler options,
                                   rive::rcp<rive::RenderBuffer> vertices_f32,
                                   rive::rcp<rive::RenderBuffer> uvCoords_f32,
                                   rive::rcp<rive::RenderBuffer> indices_u16,
                                   uint32_t vertexCount,
                                   uint32_t indexCount,
                                   rive::BlendMode blendMode,
                                   float opacity)
{
    // bind m_ohCanvas before calling these methods.
    assert(m_ohCanvas != nullptr);
    const auto *canvasImage = static_cast<const CanvasRenderImage *>(image);
    OhosPaint *ohPaint = canvasImage->ohosPaint();
    // Opacity is [0.0f..1.0f] while setAlpha() needs [0..255]
    CanvasRenderPaint::SetPaintAlpha(ohPaint, static_cast<int>(opacity * 255));
    CanvasRenderPaint::SetBlendMode(ohPaint, blendMode);

    auto shader = CanvasRenderImage::CreateBitmapShader(canvasImage->ohosBitmap());
    CanvasRenderPaint::SetShader(ohPaint, shader);

    OH_Drawing_VertexMode vertexMode = VERTEX_MODE_TRIANGLES;

    /** Set up the vertices */
    const float *vertices = static_cast<rive::DataRenderBuffer *>(vertices_f32.get())->f32s();

    std::vector<OH_Drawing_Point2D> positions(vertexCount);
    for (int i = 0; i < vertexCount; i++) {
        positions[i].x = vertices[i * 2];
        positions[i].y = vertices[i * 2 + 1];
    }

    /** Set up the uvs */
    const float *uvs = static_cast<rive::DataRenderBuffer *>(uvCoords_f32.get())->f32s();
    std::vector<OH_Drawing_Point2D> scaledUVs(vertexCount);
    for (int i = 0; i < vertexCount; i++) {
        // Need to manually scale UVs for canvas.drawVertices() to work.
        scaledUVs[i].x = uvs[i * 2] * image->width();
        scaledUVs[i].y = uvs[i * 2 + 1] * image->height();
    }

    /** Set up the indices */
    const uint16_t *indices = static_cast<rive::DataRenderBuffer *>(indices_u16.get())->u16s();
    uint32_t *no_colors = nullptr;

    OH_Drawing_CanvasDrawVertices(m_ohCanvas, vertexMode, vertexCount * 2, positions.data(), scaledUVs.data(),
                                  no_colors, indexCount, indices, RiveBlendModeToOhosBlendMode(blendMode));
}
} // namespace ohos_rive
